/** \file I2C.c
 **
 **  MFS I2C operation function file
 **
 ** History:
 **   - 2013-11-22  1.0  Mark  First Version
 *************************************************************************/


/*****************************************************************************/
/* Include files                                                             */
/*****************************************************************************/
#include "includes.h"

/*****************************************************************************/
/* Local pre-processor symbols/macros ('#define')                            */
/*****************************************************************************/
#define NO_NEW_DEFINE 0
/*****************************************************************************/
/* Global variable definitions (declared in header file with 'extern')       */
/*****************************************************************************/

/*****************************************************************************/
/* Local type definitions ('typedef')                                        */
/*****************************************************************************/

/*****************************************************************************/
/* Local variable definitions ('static')                                     */
/*****************************************************************************/
static FM3_MFS03_I2C_TypeDef* I2C_Dev[8]={
				((FM3_MFS03_I2C_TypeDef *)FM3_MFS0_I2C_BASE),
				((FM3_MFS03_I2C_TypeDef *)FM3_MFS1_I2C_BASE),
				((FM3_MFS03_I2C_TypeDef *)FM3_MFS2_I2C_BASE),
				((FM3_MFS03_I2C_TypeDef *)FM3_MFS3_I2C_BASE),
				((FM3_MFS03_I2C_TypeDef *)FM3_MFS4_I2C_BASE),
				((FM3_MFS03_I2C_TypeDef *)FM3_MFS5_I2C_BASE),
				((FM3_MFS03_I2C_TypeDef *)FM3_MFS6_I2C_BASE),
				((FM3_MFS03_I2C_TypeDef *)FM3_MFS7_I2C_BASE)};

/*****************************************************************************
 *  DESCRIPTION:    Initialize MFS1,3,6,7 to I2C mode
 *
 *  PARAMETERS:     u8I2c:channel; Baudrate: speed of I2C bus
 *
 *  RETURNS:        boolean TURE of FLASE
 *****************************************************************************/
boolean_t I2c_Master_Init(uint8_t u8I2c, uint32_t Baudrate)
{
	switch(u8I2c){
	case 1:
		FM3_GPIO->PFR5 |= (1<<7)|(1<<8);
		FM3_GPIO->PCR5 |= (1<<7)|(1<<8);
		FM3_GPIO->EPFR07 = FM3_GPIO->EPFR07 & ~((3<<12)|(3<<14)) |
							(1<<12)|(1<<14);
		break;
	case 3:
		FM3_GPIO->PFR5 |= (1<<1)|(1<<2);
		FM3_GPIO->PCR5 |= (1<<1)|(1<<2);
		FM3_GPIO->EPFR07 = FM3_GPIO->EPFR07 & ~((3<<26)|(3<<24)) |
							(2<<26)|(2<<24);
		break;
	case 6:
		FM3_GPIO->PFR5 |= (1<<4)|(1<<5);
		FM3_GPIO->PCR5 |= (1<<4)|(1<<5);
		FM3_GPIO->EPFR08 = FM3_GPIO->EPFR08 & ~((3<<16)|(3<<18)|(3<<20)) |
							(1<<16)|(1<<18)|(1<<20);
		break;
	case 7:
		FM3_GPIO->PFR5 |= (1<<10)|(1<<11);
		FM3_GPIO->PCR5 |= (1<<10)|(1<<11);
		FM3_GPIO->EPFR08 = FM3_GPIO->EPFR08 & ~((3<<24)|(3<<26)) |
							(1<<24)|(1<<26);
		break;
	default:
		return FALSE;
	}
  	I2C_Dev[u8I2c]->BGR  = I2C_BAUDRATE(Baudrate);
  	I2C_Dev[u8I2c]->IBCR |= 0x80;	// reset UART
  	I2C_Dev[u8I2c]->SMR = 0x80;		// operation mode 4, no interrupts, no test

	return TRUE;
}

/*****************************************************************************
 *  DESCRIPTION:    Start of I2C Communication with 1st Data Byte
 *
 *  PARAMETERS:     Data Byte
 *
 *  \retval      0    0k
 *  \retval      -1   Time-out Error
 *  \retval      -2   Bus Error
 *  \retval      -3   Ack Error
 *  \retval      -5   Trx Error
 *****************************************************************************/
int32_t I2cStart(uint8_t u8I2c, uint8_t u8Data)
{
  	uint16_t u16ErrorCount = 0;
	uint16_t u16Timeout;

  	I2C_Dev[u8I2c]->ISBA = 0x00;
  	I2C_Dev[u8I2c]->ISMK = 0x80;         	// ISMK_EN = 1

  	I2C_Dev[u8I2c]->TDR  = u8Data;       	// Send data to
  	I2C_Dev[u8I2c]->IBCR = 0x80;         	// MSS = 1: Master Mode

	do{
		u16Timeout = TIME_OUT_INT;

		while((!(I2C_Dev[u8I2c]->IBCR & 0x01)) // Wait for transmission complete via INT flag
			  && (u16Timeout)){
			u16Timeout--;
		}

		if (0 == u16Timeout){
		  	return -1;
		}

		if (I2C_Dev[u8I2c]->IBCR & 0x02){     // BER == 1? ->  error
		  	return -2;
		}

		if (!(I2C_Dev[u8I2c]->IBCR & 0x40)){  // ACT == 0? ->  error
		  	return -3;
		}

    	// MSS is set ...

		if (I2C_Dev[u8I2c]->IBSR & 0x40){  // RACK == 1? ->  busy or error
		  	I2C_Dev[u8I2c]->IBCR = 0xE0;  // MMS = CSS = ACKE = 1: Try restart (if busy)
		  	u16ErrorCount++;

		  	if (u16ErrorCount > TIME_OUT){
				return -1;        // too much retrials
		  	}
		}
		else{
		 	u16ErrorCount = 0;
		}

	}while (u16ErrorCount);

	if (!(I2C_Dev[u8I2c]->IBSR & 0x10)){   // TRX == 0? ->  error
		 return -5;
	  }

	return 0;
}

/**
 ******************************************************************************
 **  \brief        Send I2C Byte
 **
 **  \param [in]   u8Data   Byte Data
 **
 **  \retval      0    0k
 **  \retval      -1   Time-out Error
 **  \retval      -2   Bus Error
 **  \retval      -3   Ack Error
 **  \retval      -4   Rack Error
 **  \retval      -5   Trx Error
 ******************************************************************************/
int32_t I2cSendByte(uint8_t u8I2c, uint8_t u8Data)
{
  	uint16_t u16Timeout;

  	I2C_Dev[u8I2c]->TDR = u8Data;  // Send data to slave
  	I2C_Dev[u8I2c]->IBCR = 0xB0;   // WSEL = 1, ACKE = 1, Clear INT flag

  	u16Timeout = TIME_OUT_INT;

  	while((!(I2C_Dev[u8I2c]->IBCR & 0x01)) // Wait for transmission complete via INT flag
        && (u16Timeout))
	{
		u16Timeout--;
	}

	if (0 == u16Timeout)
	{
		return -1;
	}

	if (I2C_Dev[u8I2c]->IBCR & 0x02)      // BER == 1? ->  error
	{
		return -2;
	}

	if (!(I2C_Dev[u8I2c]->IBCR & 0x40))   // ACT == 0? ->  error
	{
		return -3;
	}

	// MSS is set, no reserved address

	if (I2C_Dev[u8I2c]->IBSR & 0x40)      // RACK == 1? ->  error
	{
		return -4;
	}

	if (!(I2C_Dev[u8I2c]->IBSR & 0x10))   // TRX == 0? ->  error
	{
		return -5;
	}

	return 0;
}

/**
 ******************************************************************************
 **  \brief        Continue I2C Byte (Iteration)
 **
 **  \param [in]   u8Data   Byte Data
 **
 **  \retval      0    0k
 **  \retval      -1   Time-out Error
 **  \retval      -2   Bus Error
 **  \retval      -3   Ack Error
 **  \retval      -4   Rack Error
 **  \retval      -5   Trx Error
 ******************************************************************************/
int32_t I2cContinueSendByte(uint8_t u8I2c, uint8_t u8Data)
{
	uint16_t u16Timeout;

	I2C_Dev[u8I2c]->TDR = u8Data;  // Send data to slave
	I2C_Dev[u8I2c]->IBCR = 0xE0;   // Set continuous mode MSS = SCC = ACKE = 1

	u16Timeout = TIME_OUT_INT;

	while((!(I2C_Dev[u8I2c]->IBCR & 0x01)) // Wait for transmission complete via INT flag
	    && (u16Timeout))
	{
		u16Timeout--;
	}

	if (0 == u16Timeout)
	{
		return -1;
	}

	if (I2C_Dev[u8I2c]->IBCR & 0x02)      // BER == 1? ->  error
	{
		return -1;
	}

	if (!(I2C_Dev[u8I2c]->IBCR & 0x40))   // ACT == 0? ->  error
	{
		return -3;
	}

	// MSS is set, no reserved address

	if (I2C_Dev[u8I2c]->IBSR & 0x40)      // RACK == 1? ->  error
	{
		return -4;
	}

	if (u8Data & 0x01)                  // Read = 0, Write = 1
	{
		if (I2C_Dev[u8I2c]->IBSR & 0x10)    // TRX != (data & 0x01) ? ->  error
		{
			return -5;
		}
	}
	else
	{
		if (!(I2C_Dev[u8I2c]->IBSR & 0x10)) // TRX == (data & 0x01) ? ->  error
		{
			return -5;
		}
	}

	return 0;
}

/**
 ******************************************************************************
 **  \brief    Stop I2C Write Communication
 **
 **  \param    None
 **
 **  \retval      0    0k
 **  \retval      -1   Time-out Error
 ******************************************************************************/
uint16_t I2cWriteStop(uint8_t u8I2c)
{
	uint16_t u16Timeout;
	I2C_Dev[u8I2c]->IBCR = 0x20;    // MMS = CSS = INT = 0, ACKE = 1

	u16Timeout = TIME_OUT_INT;// wait transmission complete
	while((1 == (I2C_Dev[u8I2c]->IBSR & 0x01)) && (u16Timeout)){
		u16Timeout--;
	}
	if(u16Timeout==0){  // timeout
		return -1;
	}
	return 0;
}

/**
 ******************************************************************************
 **  \brief    Stop I2C Read Communication
 **
 **  \param    None
 **
 **  \retval      0    0k
 **  \retval      -1   Time-out Error
 ******************************************************************************/
uint16_t I2cReadStop(uint8_t u8I2c)
{
	uint16_t u16Timeout;
	I2C_Dev[u8I2c]->IBCR = 0x01;     // INT = 1, MMS = ACKE = 0
	u16Timeout = TIME_OUT_INT;// wait transmission complete
	while((1 == (I2C_Dev[u8I2c]->IBSR & 0x01)) && (u16Timeout)){
		u16Timeout--;
	}
	if(u16Timeout==0){  // timeout
		return -1;
	}
	return 0;
}
/**
 ******************************************************************************
 **  \brief     Receive I2C Byte
 **
 **  \param     None
 **
 **  \return    Received Data (positive value)
 **  \return    -1 = Time-out Error
 **  \return    -2 = Bus Error
 **  \return    -3 = Ack Error
 **  \return    -4 = Rack Error
 **  \return    -5 = Trx Error
 ******************************************************************************/
int8_t I2cReceiveByte(uint8_t u8I2c)
{
	uint8_t  u8Data;
	uint16_t u16ErrorCount = 1;
	uint16_t u16Timeout;

	do
	{
		if ((!(I2C_Dev[u8I2c]->IBSR & 0x80)))   // FBT = 1
		{
		  u8Data = I2C_Dev[u8I2c]->RDR;
		  u16ErrorCount = 0;
		}
		else
		{
		  	u16ErrorCount++;
			if (u16ErrorCount > TIME_OUT) // too much retrails
			{
			    return -1;
			}

		  	I2C_Dev[u8I2c]->IBCR = 0xB0;   // MMS = 1, WSEL = 1, ACKE = 1, Clear INT flag

		  	u16Timeout = TIME_OUT_INT;

			while((!(I2C_Dev[u8I2c]->IBCR & 0x01)) // Wait for transmission complete via INT flag
			    && (u16Timeout))
			{
				u16Timeout--;
			}

		  	if (0 == u16Timeout)
		  	{
		    	return -1;
		  	}

		  	if (I2C_Dev[u8I2c]->IBCR & 0x02)       // BER == 1? ->  error
		  	{
		    	return -2;
		  	}

		  	if (!(I2C_Dev[u8I2c]->IBCR & 0x40))    // ACT == 0? ->  error
		  	{
		    	return -3;
		  	}

		  	// MSS is set, no reserved address

		  	if (I2C_Dev[u8I2c]->IBSR & 0x40)       // RACK == 1? ->  error
		  	{
		    	return -4;
		  	}

		  	if (I2C_Dev[u8I2c]->IBSR & 0x10)       // TRX == 1? ->  error
		  	{
		    	return -5;
		  	}
		}
	}while(u16ErrorCount);

	return (int16_t)u8Data;
}

/**
 ******************************************************************************
 **  \brief     Receive I2C Byte sequentially (with dummy TX data for generating
 **             clock)
 **
 **  \param [in] u8LastByte   1 == indicates last byte to read w/o TX dummy data
 **
 **  \return    Received Data (positive value)
 **  \return    -1 = Time-out Error
 **  \return    -2 = Bus Error
 **  \return    -3 = Ack Error
 **  \return    -4 = Rack Error
 **  \return    -5 = Trx Error
 ******************************************************************************/
int8_t I2cReceiveByteSeq(uint8_t u8I2c, uint8_t u8LastByte)
{
	uint8_t  u8Data;
  	uint16_t u16ErrorCount = 1;
  	uint16_t  u16Timeout;

	do
	{
		if ((!(I2C_Dev[u8I2c]->IBSR & 0x80))   // FBT == 0
		    && (I2C_Dev[u8I2c]->SSR & 0x04))   // RDRF == 1
		{
		  u8Data = I2C_Dev[u8I2c]->RDR;
		  u16ErrorCount = 0;
		}
		else
		{
		  	u16ErrorCount++;
		  	if (u16ErrorCount > TIME_OUT) // too much retrails
		  	{
		    	return -1;
		  	}

		  	I2C_Dev[u8I2c]->IBCR = 0xA0;   // MSS = 1, ACKE = 1, Clear INT flag

		  	u16Timeout = TIME_OUT_INT;

		  	while((!(I2C_Dev[u8I2c]->SSR & 0x04)) // SSR_RDRF == 1?
		        && (u16Timeout))
		  	{
		    	u16Timeout--;
		  	}

		 	if (0 == u16Timeout)
		  	{
		    	return -1;
		  	}

		  	if (1 != u8LastByte)
		  	{
		    	I2C_Dev[u8I2c]->TDR = 0;  // Send dummy TX data if not last byte
		  	}

		  	if (I2C_Dev[u8I2c]->IBCR & 0x02)       // BER == 1? ->  error
		  	{
		    	return -2;
		  	}

		  	if (!(I2C_Dev[u8I2c]->IBCR & 0x40))    // ACT == 0? ->  error
		  	{
		    	return -3;
		  	}

		  	// MSS is set, no reserved address

		  	if (I2C_Dev[u8I2c]->IBSR & 0x40)       // RACK == 1? ->  error
		  	{
		    	return -4;
		  	}

		  	if (I2C_Dev[u8I2c]->IBSR & 0x10)       // TRX == 1? ->  error
		  	{
		    	return -5;
		  	}
		}
	}while(u16ErrorCount);

	return (int16_t)u8Data;
}


/** Read multiple bytes from an 8-bit device register.
 * @param ch MFS channel of MCU
 * @param devAddr I2C slave device address
 * @param regAddr First register regAddr to read from
 * @param length Number of bytes to read
 * @param data Buffer to store read data in
 * timeout Optional read timeout in milliseconds, use default value in i2c.h
 * @return Number of bytes read (0 indicates failure)
 */
int8_t I2c_readBytes(uint8_t ch, uint8_t devAddr,
				uint8_t regAddr, uint8_t length, uint8_t *data)
{
	uint8_t count;
	count = length;
	if (-1 == I2cStart(ch, devAddr | I2C_WRITE))//snd dev addr
	{
    		return 0;
	}

	if (-1 == I2cSendByte(ch,regAddr))             //snd reg addr
	{
		return 0;
	}

	if (-1 == I2cContinueSendByte(ch, devAddr | I2C_READ))
	{
		return 0;
	}
	#if NO_NEW_DEFINE
	while(length--)
	{
		*data++ = I2cReceiveByte(ch, length);
	}
	#else
	while(--length)
	{
		*data++ = I2cReceiveByteSeq(ch, 0);
	}
	*data++ = I2cReceiveByteSeq(ch, 1);
	#endif
	I2cReadStop(ch);
	return count;
}
/** Read a byte from an 8-bit device register.
 * @param ch MFS channel of MCU
 * @param devAddr I2C slave device address
 * @param regAddr First register regAddr to read from
 * @param length Number of bytes to read
 * @param data Buffer to store read data in
 * timeout Optional read timeout in milliseconds, use default value in i2c.h
 * @return Number of bytes read (0 indicates failure)
 */
int8_t I2c_readByte(uint8_t ch, uint8_t devAddr, uint8_t regAddr, uint8_t *u8Data)
{
	return I2c_readBytes(ch, devAddr,  regAddr, 1, u8Data);
}

/** Write multiple bytes to device register.
 * @param ch MFS channel of MCU
 * @param devAddr I2C slave device address
 * @param regAddr First register regAddr to write to
 * @param length Number of bytes to write
 * @param data Buffer to store write data
 * timeout Optional write timeout in milliseconds, use default value in i2c.h
 * @return Status of operation (TRUE = success)

 */
boolean_t I2c_writeBytes(uint8_t ch, uint8_t devAddr, uint8_t regAddr,
		uint8_t length, uint8_t* data)
{
	if (-1 == I2cStart(ch, devAddr | I2C_WRITE)){
		return FALSE;
	}
	if (-1 == I2cSendByte(ch, regAddr)){
		return FALSE;
	}
	while(length--)
	{
		if (-1 == I2cSendByte(ch, *data++))
		{
			return FALSE;
		}
	}
   I2cWriteStop(ch);
   //udelay(1000);
   return TRUE;
}


/** Write single byte to an 8-bit device register.
 * @param ch MFS channel of MCU
 * @param devAddr I2C slave device address
 * @param regAddr Register address to write to
 * @param data New byte value to write
 * @return Status of operation (true = success)
 */
boolean_t I2c_writeByte(uint8_t ch, uint8_t devAddr, uint8_t regAddr, uint8_t data) {

    return I2c_writeBytes(ch, devAddr, regAddr, 1, &data);

}

/** Write multiple bits in an 8-bit device register.
 * @param ch MFS channel of MCU
 * @param devAddr I2C slave device address
 * @param regAddr Register regAddr to write to
 * @param bitStart First bit position to write (0-7)
 * @param length Number of bits to write (not more than 8)
 * @param data Right-aligned value to write
 * @return Status of operation (true = success)
 */
boolean_t I2c_writeBits(uint8_t ch, uint8_t devAddr, uint8_t regAddr,
					uint8_t bitStart, uint8_t length, uint8_t data) {
    // 010 value to write
    // 76543210 bit numbers
    // xxx   args: bitStart=4, length=3
    // 01000000 shift left (8 - length)    ]
    // 00001000 shift right (7 - bitStart) ] --- two shifts ensure all non-important bits are 0
    // 11100011 mask byte
    // 10101111 original value (sample)
    // 10100011 original & mask
    // 10101011 masked | value
    uint8_t b;
    if (I2c_readByte(ch, devAddr, regAddr, &b) != 0) {
        //uint8_t mask = (0xFF << (8 - length)) | (0xFF >> (bitStart + length - 1));
        uint8_t mask = (0xFF << (bitStart + 1)) | 0xFF >> ((8 - bitStart) + length - 1);
        data <<= (8 - length);
        data >>= (7 - bitStart);
        b &= mask;
        b |= data;
        return I2c_writeByte(ch, devAddr, regAddr, b);
    } else {
        return FALSE;
    }
}

/** write a single bit in an 8-bit device register.
 * @param ch MFS channel of MCU
 * @param devAddr I2C slave device address
 * @param regAddr Register regAddr to write to
 * @param bitNum Bit position to write (0-7)
 * @param value New bit value to write
 * @return Status of operation (true = success)
 */
boolean_t I2c_writeBit(uint8_t ch, uint8_t devAddr, uint8_t regAddr,
				uint8_t bitNum, uint8_t data)
{
    uint8_t b;
    I2c_readByte(ch, devAddr, regAddr, &b);
    b = (data != 0) ? (b | (1 << bitNum)) : (b & ~(1 << bitNum));
    return I2c_writeByte(ch, devAddr, regAddr, b);
}

void I2cRxIrqHandler(uint8_t mfsn)
{


}
